#include "../server.h"

#define HASH_MASK               0x7f
#define HASH_SIZE               (HASH_MASK+1)
#define HASH_USER_MAX_NUM       50000

#define SID_TIME_SIZE           10
#define CONFIRM_SIZE            4
#define UUID_MAX_SIZE           50
#define SID_MIN_SIZE            (SID_TIME_SIZE+CONFIRM_SIZE+1)
#define SID_MAX_SIZE            (SID_TIME_SIZE+CONFIRM_SIZE+UUID_MAX_SIZE)
#define SIDENC_MAX_SIZE         (SID_MAX_SIZE*2)
#define SID_FMT                 "%010X%04X%s"

#define MAP_ALLOC_NUM           5
#define MAP_VAL_MAX_SIZE        PARA_ITEM_MAX_SIZE
#define MAP_MAX_NUM             200

struct _map
{/*{{{*/
    char *key;
    char *val;
};/*}}}*/

struct _user
{/*{{{*/
    char uuid[UUID_MAX_SIZE+1];
    unsigned short confirm;
    time_t sid_time;                    /* session create time */
    time_t accessed;                    /* last accessed time */
    struct _map *maps;
    short map_alloc_num;
    struct rb_link tree; 
    struct listp_link list;
};/*}}}*/

struct _hash
{/*{{{*/
    int idx;
    pthread_mutex_t lock;
    struct rb_tree tree;
    struct listp_link list;
};/*}}}*/

static OSSL_KEY sesskey;
static struct _hash *hashes;

static int _is_int(char *str)
{/*{{{*/
    int i;
    unsigned int len;

    if(!str)
        return(IS_FALSE);
    len=strlen(str);
    if(len == 0)
        return(IS_FALSE);
    if(len > LONG_NUMBER_SIZE)
        return(-1);
    if(len == 1 && (str[0] == '-' || str[0] == '+'))
        return(IS_FALSE);
    if((str[0] < '0' || str[0] > '9') &&
            (str[0] != '-' && str[0] != '+'))
        return(IS_FALSE);
    for(i=1; i<len; i++)
    {
        if(str[i] < '0' || str[i] > '9')
            return(IS_FALSE);
    }
    return(IS_TRUE);
}/*}}}*/

static int _construct_sid(int hash_idx, time_t sid_time,
        unsigned short confirm, char *uuid, char *sid)
{/*{{{*/
    int res;

    char plain[SID_MAX_SIZE+1]; 
    unsigned char enc[SIDENC_MAX_SIZE+1]; 
    int enc_size;
    sprintf(plain, SID_FMT, (unsigned int)sid_time, confirm, uuid);
    res=ossl_sym_encrypt(sesskey,
            (unsigned char *)plain, strlen(plain),
            enc, &enc_size);
    if(res)
    {
        errlog("%s: ossl_sym_encrypt fail, res:%d",
                __func__, res);
        return(res);
    }
//    expand_to_str((unsigned char *)sid+SN_SESSION_SID_HASH_SIZE,
//            enc_size, sid+SN_SESSION_SID_HASH_SIZE);
    char code[SIDENC_MAX_SIZE+1]; 
    int code_size;
    res=ossl_encode(OSSL_ENCODE_BASE64,
            enc, enc_size, code, &code_size);
    if(res)
    {
        errlog("%s: ossl_encode fail, res:%d",
                __func__, res);
        return(res);
    }
    base64_unwrap(code, sid);

    return(0);
}/*}}}*/

static int _crack_sid(char *sid, time_t *sid_time,
        unsigned short *confirm, char *uuid, struct _hash **hashp)
{/*{{{*/
    int res;

    char code[SIDENC_MAX_SIZE+1];
    unsigned char enc[SIDENC_MAX_SIZE+1];
    int enc_size;
    base64_wrap(sid, code);
    res=ossl_decode(OSSL_ENCODE_BASE64, code, strlen(code),
            enc, &enc_size);
    if(res || enc_size < SID_MIN_SIZE || enc_size > SID_MAX_SIZE)
    {
        errlog("%s: ossl_decode fail, enc_size:%d res:%d",
                __func__, enc_size, res);
        return(-1);
    }
    //if(!collapse_from_str(sid+SN_SESSION_SID_HASH_SIZE, enc_size*2,
    //            enc))
    //{   return(-1);   }
    char plain[SIDENC_MAX_SIZE+1];
    int plain_size;
    res=ossl_sym_decrypt(sesskey, enc, enc_size,
            (unsigned char *)plain, &plain_size);
    if(res)
    {
        errlog("%s: ossl_sym_decrypt fail, res:%d, BAD",
                __func__, res);
        return(res);
    }
    plain[plain_size]=ZERO;

    char buf[SID_MAX_SIZE+1];
    strncpy(buf, plain, SID_TIME_SIZE);
    buf[SID_TIME_SIZE]=ZERO;
    *sid_time=(time_t)strtol(buf, NULL, 16);
    strncpy(buf, plain+SID_TIME_SIZE, CONFIRM_SIZE);
    buf[CONFIRM_SIZE]=ZERO;
    *confirm=(unsigned short)strtol(buf, NULL, 16);
    strcpy(uuid, plain+SID_TIME_SIZE+CONFIRM_SIZE);

	int hash_idx=get_hash((unsigned char *)uuid, strlen(uuid)) &
        HASH_MASK;
    *hashp=&hashes[hash_idx];

    return(0);
}/*}}}*/

static char *_user_create(struct _hash *hash, char *uuid, char *sid,
        struct _user **userp)
{/*{{{*/
    struct _user *user;
    int res;
    int i;

    ALLOC_DOERR(user, struct _user,
            errlog("%s: alloc user fail, BAD", __func__);
            return(SOAR_ERR_RESOURCE));
    strcpy(user->uuid, uuid);
    user->confirm=(unsigned short)random_int(1, 0xffff, NULL);
    user->sid_time=user->accessed=curr_timestamp(NULL);
    ALLOC_ARRAY_DOERR(user->maps, struct _map, MAP_ALLOC_NUM,
            user->map_alloc_num,
            errlog("%s %s: alloc user->maps fail, BAD",
                __FILE__, __func__);
            free(user);
            return(SOAR_ERR_RESOURCE));
    for(i=0; i<MAP_ALLOC_NUM; i++)
        user->maps[i].key=user->maps[i].val=(char *)NULL;
    if((res=_construct_sid(hash->idx, user->sid_time,
                    user->confirm, uuid, sid)))
    {
        errlog("%s %s: _construct_sid fail, res:%d, IMPOSSIBLE",
                __FILE__, __func__, res);
        free(user->maps); free(user);
        return(SOAR_ERR_SESSION_SID_INVALID);
    }

    // 插入
    tree_setkey_string(user, uuid, tree);
    listp_init(&user->list);
    if((res=rb_insert(&hash->tree, &user->tree)))
    {
        errlog("%s %s: insert uuid(%s) into hash fail, res:%d, BAD",
                __FILE__, __func__, uuid, res);
        free(user->maps); free(user);
        return(SOAR_ERR_SESSION_CREATE_FAIL);
    }
    listp_add(&user->list, &hash->list);

    *userp=user;
    return(SOAR_ERR_OK);
}/*}}}*/

static char *_user_free(struct _hash *hash, struct _user *user)
{/*{{{*/
	int i;

    for(i=0; i<user->map_alloc_num; i++)
    {
        FREE(user->maps[i].key);
        FREE(user->maps[i].val);
    }
    FREE(user->maps);
    if(!rb_delete_link(&hash->tree, &user->tree))
    {
        errlog("%s %s: delete uuid(%s) from hash fail, IMPOSSIBLE",
                __FILE__, __func__, user->uuid);
        return(SOAR_ERR_RESOURCE);
    }
    else
    {   listp_del(&user->list);   }
    free(user);
    return(SOAR_ERR_OK);
}/*}}}*/

static int _user_timeout(struct _hash *hash, struct _user *user)
{/*{{{*/
    time_t now=curr_timestamp(NULL);

    if(now - user->accessed >
            cnfg_info._session_.access_timeout)
    {
        errlog("%s: Session(%s) TIMEOUT", __func__, user->uuid);
        _user_free(hash, user);
        return(IS_TRUE);
    }
    else
    {
        user->accessed=now;
        listp_del(&user->list);
        listp_add(&user->list, &hash->list);
        return(IS_FALSE);
    }
}/*}}}*/

static void _eliminate_timeout(struct _hash *hash, int *user_num)
{/*{{{*/
    int i;
    struct _user *user;
    time_t now=curr_timestamp(NULL);

    for(i=0; i<3; i++)
    {
        if(listp_is_empty(&hash->list)) break;
        user=listp_entry(hash->list.prev, struct _user, list);
        if(now - user->accessed < cnfg_info._session_.access_timeout)
                //hash->tree.num < HASH_USER_MAX_NUM)
            break;
        if(cnfg_info._errlog_.tracking)
            errlog("%s %s: uuid(%s) TIMEOUT",
                    __FILE__, __func__, user->uuid);
        _user_free(hash, user);
    }
    if(user_num) *user_num=hash->tree.num;
}/*}}}*/

static char *_imp_put_key(struct _user *user, char *key, char *val)
{/*{{{*/
    struct _map *map;
	int key_idx;
    int empty_idx=-1;

    for(key_idx=0; key_idx<user->map_alloc_num; key_idx++)
    {
        map=&user->maps[key_idx];
        if(map->key)
        {
            if(strcmp(map->key, key) == 0)
            {   // 已有key
                if(BUF_IS_CLR(val))
                {
                    FREE(map->val);
                    map->val=(char *)NULL;
                }
                else
                {
                    char *p;
                    if(!(p=strdup(val)))
                    {
                        errlog("%s %s: uuid(%s) alloc val(%s)",
                                __FILE__, __func__, user->uuid, val);
                        return(SOAR_ERR_RESOURCE);
                    }
                    FREE(map->val);
                    map->val=p;
                }
                if(cnfg_info._errlog_.tracking)
                    errlog("%s %s: uuid(%s) overwrite key(%s)->val(%s)",
                            __FILE__, __func__, user->uuid, key, val);
                return(SOAR_ERR_OK);
            }
        }
        else
        {
            if(empty_idx == -1)
                empty_idx=key_idx;
        }
    }

    // 新增key
    if(empty_idx == -1)
    {
        if(user->map_alloc_num >= MAP_MAX_NUM)
            return(SOAR_ERR_SESSION_KEY_FULL);
        EXPAND_ARRAY_DOERR(user->maps, struct _map, MAP_ALLOC_NUM,
                user->map_alloc_num,
                errlog("%s %s: expand uuid(%s) map fail",
                    __FILE__, __func__, user->uuid);
                return(SOAR_ERR_RESOURCE));
        for(key_idx=user->map_alloc_num-MAP_ALLOC_NUM;
                key_idx<user->map_alloc_num; key_idx++)
            user->maps[key_idx].key=user->maps[key_idx].val=(char *)NULL;
        map=&user->maps[user->map_alloc_num-MAP_ALLOC_NUM];
    }
    else
    {   map=&user->maps[empty_idx];   }
    if(!(map->key=strdup(key)))
    {
        errlog("%s %s: uuid(%s) alloc key(%s) fail",
                __FILE__, __func__, user->uuid, key);
        return(SOAR_ERR_RESOURCE);
    }
    if(BUF_IS_CLR(val))
    {   map->val=(char *)NULL;   }
    else
    {
        if(!(map->val=strdup(val)))
        {
            errlog("%s %s: uuid(%s) alloc val(%s) fail",
                    __FILE__, __func__, user->uuid, val);
            FREE(map->key);
            return(SOAR_ERR_RESOURCE);
        }
    }
    if(cnfg_info._errlog_.tracking)
        errlog("%s %s: uuid(%s) new key(%s)->val(%s)",
                __FILE__, __func__, user->uuid, key, val);

    return(SOAR_ERR_OK);
}/*}}}*/

static char *_imp_get_key(struct _user *user, char *key, char *val)
{/*{{{*/
    struct _map *map;
	int key_idx;

    CLR_BUF(val);
    for(key_idx=0; key_idx<user->map_alloc_num; key_idx++)
    {
        map=&user->maps[key_idx];
        if(map->key)
        {
            if(strcmp(map->key, key) == 0)
            {
                strcpy(val, map->val?map->val:ZEROS);
                return(SOAR_ERR_OK);
            }
        }
    }
    return(SOAR_ERR_SESSION_KEY_NOT_FOUND);
}/*}}}*/

static char *_imp_mod_key(struct _user *user, char *key, int delta)
{/*{{{*/
    struct _map *map;
    int key_idx;
	char val[LONG_NUMBER_SIZE+1];

    for(key_idx=0; key_idx<user->map_alloc_num; key_idx++)
    {
        map=&user->maps[key_idx];
        if(map->key)
        {
            if(strcmp(map->key, key) == 0)
            {
                char *p;
                if(map->val)
                {
                    if(!_is_int(map->val))
                        return(SOAR_ERR_SESSION_VAL_NOT_INT);
                    sprintf(val, "%d", atoi(map->val)+delta);
                }
                else
                {
                    sprintf(val, "%d", delta);
                }
                if(!(p=strdup(val)))
                    return(SOAR_ERR_RESOURCE);
                if(map->val)
                    free(map->val);
                map->val=p;
                return(SOAR_ERR_OK);
            }
        }
    }
    return(SOAR_ERR_SESSION_KEY_NOT_FOUND);
}/*}}}*/

static char *_imp_del_key(struct _user *user, char *key)
{/*{{{*/
	int key_idx;
	struct _map *map;

    for(key_idx=0; key_idx<user->map_alloc_num; key_idx++)
    {
        map=&user->maps[key_idx];
        if(map->key)
        {
            if(strcmp(map->key, key) == 0)
            {
                FREE(map->val);
                FREE(map->key);
                return(SOAR_ERR_OK);
            }
        }
    }
    return(SOAR_ERR_SESSION_KEY_NOT_FOUND);
}/*}}}*/

int session_init(int *hash_num)
{/*{{{*/
    int res;

    char password[15+1];
    random_password(password, sizeof(password)-1);
    if((res=ossl_sym_load_key(OSSL_SYM_AES_128_CBC, OSSL_SYM_PASSWD,
                    password, &sesskey)))
    {
        errlog("%s: ossl_sym_load_key fail, res:%d",
                __func__, res);
        return(-1);
    }

    ALLOC_MULTI_RETERR(hashes, struct _hash, HASH_SIZE,
            RET_ERR_ALLOC);
    int i;
    for(i=0; i<HASH_SIZE; ++i)
    {
        struct _hash *hash=&hashes[i];
        hash->idx=i;
        pthread_mutex_init(&hash->lock, NULL);
        rb_create(TREE_KEY_STRING, &hash->tree);
        listp_init(&hash->list);
    }

    *hash_num=HASH_SIZE;
    return(0);
}/*}}}*/

SVC_FUNC(CREATE)
{/*{{{*/
    char para_desc[50];
    PARA para;
    char *rtn;
    char sid[SIDENC_MAX_SIZE+1];
	char uuid[UUID_MAX_SIZE+1];
    struct _hash *hash;
    struct rb_link *tree;
    struct _user *user;
    int res;

    sprintf(para_desc, "uuid(string:1-%d)", UUID_MAX_SIZE);
    if((res=para_load(body, para_desc, &para)))
	{
        if(res > 0)
            errlog("%s: msg(%s) no.%d para invalid",
                    __func__, body, res);
        else
            errlog("%s: para_load msg(%s) fail, res:%d",
                    __func__, body, res);
		para_put_item(msg_out, SOAR_ERR_PARA);
        return(atoi(SOAR_ERR_PARA));
	}
    para_copy_by_name(para, "uuid", uuid);
    trim_tail_space_notnull(uuid);
    para_free(para);

    int hash_idx=get_hash((unsigned char *)uuid, strlen(uuid)) &
        HASH_MASK;
    hash=&hashes[hash_idx];

	pthread_mutex_lock(&hash->lock);

    tree=rb_find(&hash->tree, uuid);
    if(tree)
    {
        user=tree_entry(tree, struct _user, tree);
        if(_user_timeout(hash, user))  // 超时
        {
            if(cnfg_info._errlog_.tracking)
                errlog("%s %s: uuid(%s) timeout, not found"
                        __FILE__, __func__, uuid);
        }
        else
        {
            res=_construct_sid(hash->idx, user->sid_time,
                    user->confirm, uuid, sid);
            if(res)
            {
                errlog("%s: _construct_sid fail, res:%d",
                        __func__, res);
                rtn=SOAR_ERR_SESSION_SID_INVALID;
            }
            else
            {
                rtn=SOAR_ERR_SESSION_EXIST;
                para_put_item(msg_out, sid);
            }
            goto UNLOCK;
        }
    }
    // 创建
    rtn=_user_create(hash, uuid, sid, &user);
    if(rtn != (char *)SOAR_ERR_OK)
    {
        errlog("%s %s: _user_create fail, res:%s",
                __FILE__, __func__, rtn);
        goto UNLOCK;
    }
    if(cnfg_info._errlog_.tracking)
        errlog("%s %s: insert uuid(%s) into hash",
                __FILE__, __func__, uuid);
    rtn=SOAR_ERR_OK;
    para_put_item(msg_out, sid);

UNLOCK:
    _eliminate_timeout(hash, &res);
    pthread_mutex_unlock(&hash->lock);

#ifdef _MY_DEBUG
    pthread_mutex_lock(&share_info.worker_info_lock);
    share_info.hash_users[hash->idx]=res;
    pthread_mutex_unlock(&share_info.worker_info_lock);
#endif

    para_insert_item(msg_out, rtn);
    return(atoi(rtn));
}/*}}}*/

SVC_FUNC(GET)
{/*{{{*/
    char para_desc[50];
    PARA para;
    char *rtn;
	char uuid[UUID_MAX_SIZE+1];
    char sid[SIDENC_MAX_SIZE+1];
	int hash_idx;
    struct _hash *hash;
    struct _user *user;
    struct rb_link *tree;
    int res;

    sprintf(para_desc, "uuid(string:1-%d)", UUID_MAX_SIZE);
    if((res=para_load(body, para_desc, &para)))
	{
        if(res > 0)
            errlog("%s: msg(%s) no.%d para invalid",
                    __func__, body, res);
        else
            errlog("%s: para_load msg(%s) fail, res:%d",
                    __func__, body, res);
        para_put_item(msg_out, SOAR_ERR_PARA);
		return(atoi(SOAR_ERR_PARA));
	}
    para_copy_by_name(para, "uuid", uuid);
    trim_tail_space_notnull(uuid);
    para_free(para);

	hash_idx=get_hash((unsigned char *)uuid, strlen(uuid)) &
        HASH_MASK;
    hash=&hashes[hash_idx];

	pthread_mutex_lock(&hash->lock);

    if((tree=rb_find(&hash->tree, uuid)))
    {
        user=tree_entry(tree, struct _user, tree);
        if(_user_timeout(hash, user) == IS_TRUE)  // 超时
        {
            if(cnfg_info._errlog_.tracking)
                errlog("%s: uuid(%s) timeout, not found",
                        __func__, uuid);
            rtn=SOAR_ERR_SESSION_NOT_FOUND;
        }
        else
        {
            if((res=_construct_sid(hash->idx, user->sid_time,
                            user->confirm, uuid, sid)))
            {
                errlog("%s: _construct_sid fail, res:%d",
                        __func__, res);
                rtn=SOAR_ERR_SESSION_SID_INVALID;
            }
            else
            {
                rtn=SOAR_ERR_OK;
                para_put_item(msg_out, sid);
            }
        }
    }
    else
    {
        rtn=SOAR_ERR_SESSION_NOT_FOUND;
        if(cnfg_info._errlog_.tracking)
            errlog("%s: uuid(%s) not found",
                    __func__, uuid);
    }

	pthread_mutex_unlock(&hash->lock);

    para_insert_item(msg_out, rtn);
    if(rtn == (char *)SOAR_ERR_SESSION_NOT_FOUND)
    {
        if(cnfg_info._errlog_.tracking)
            return(atoi(rtn));
        else
            return(atoi(SOAR_ERR_OK));
    }
    return(atoi(rtn));
}/*}}}*/

SVC_FUNC(VALIDATE)
{/*{{{*/
    char para_desc[50];
    PARA para;
    char *rtn;
	char uuid[UUID_MAX_SIZE+1];
	char sid[SIDENC_MAX_SIZE+1];
    struct _hash *hash;
    struct _user *user;
    struct rb_link *tree;
	time_t sid_time;
    unsigned short confirm;
    int res;

    sprintf(para_desc, "sid(string:20-%d)", SIDENC_MAX_SIZE);
    res=para_load(body, para_desc, &para);
    if(res)
	{
        if(res > 0)
            errlog("%s: body(%s) no.%d para invalid",
                    __func__, body, res);
        else
            errlog("%s: para_load body(%s) fail, res:%d",
                    __func__, body, res);
		para_put_item(msg_out, SOAR_ERR_PARA);
		return(atoi(SOAR_ERR_PARA));
	}
    para_copy_by_name(para, "sid", sid);
    para_free(para);

	res=_crack_sid(sid, &sid_time, &confirm, uuid, &hash);
	if(res)
    {
        errlog("%s: _crack_sid(%s) fail, res:%d",
                __func__, sid, res);
		para_put_item(msg_out, SOAR_ERR_SESSION_SID_INVALID);
		return(atoi(SOAR_ERR_SESSION_SID_INVALID));
    }

    pthread_mutex_lock(&hash->lock);

    if((tree=rb_find(&hash->tree, uuid)))
    {
        user=tree_entry(tree, struct _user, tree);
        if(_user_timeout(hash, user) == IS_TRUE)
        {
            if(cnfg_info._errlog_.tracking)
                errlog("%s %s: uuid(%s) timeout, not found",
                        __FILE__, __func__, uuid);
            rtn=SOAR_ERR_SESSION_NOT_FOUND;
        }
        else if(user->sid_time != sid_time || user->confirm != confirm)
        {
            if(cnfg_info._errlog_.tracking)
                errlog("%s %s: uuid(%s) discord, "
                        "sid_time(%08X)-(%08X) confirm(%04X)-(%04X)",
                        __FILE__, __func__, uuid,
                        user->sid_time, sid_time,
                        user->confirm, confirm);
            rtn=SOAR_ERR_SESSION_SID_DISCORD;
        }
        else
        {   rtn=SOAR_ERR_OK;   }
    }
    else
    {
        if(cnfg_info._errlog_.tracking)
            errlog("%s %s: uuid(%s) not found",
                    __FILE__, __func__, uuid);
        rtn=SOAR_ERR_SESSION_NOT_FOUND;
    }

    _eliminate_timeout(hash, &res);
    pthread_mutex_unlock(&hash->lock);

#ifdef _MY_DEBUG
    pthread_mutex_lock(&share_info.worker_info_lock);
    share_info.hash_users[hash->idx]=res;
    pthread_mutex_unlock(&share_info.worker_info_lock);
#else
    if(random_int(0, 9, NULL) == 0)
    {
        pthread_mutex_lock(&share_info.worker_info_lock);
        share_info.hash_users[hash->idx]=res;
        pthread_mutex_unlock(&share_info.worker_info_lock);
    }
#endif

    para_insert_item(msg_out, rtn);
    return(atoi(rtn));
}/*}}}*/

SVC_FUNC(DESTROY)
{/*{{{*/
    char para_desc[50];
    PARA para;
    char *rtn;
	char uuid[UUID_MAX_SIZE+1];
	char sid[SIDENC_MAX_SIZE+1];
    struct _hash *hash;
    struct _user *user;
    struct rb_link *tree;
	time_t sid_time;
    unsigned short confirm;
    int res;

    sprintf(para_desc, "sid(string:20-%d)", SIDENC_MAX_SIZE);
    res=para_load(body, para_desc, &para);
    if(res)
	{
        if(res > 0)
            errlog("%s: body(%s) no.%d para invalid",
                    __func__, body, res);
        else
            errlog("%s: para_load body(%s) fail, res:%d",
                    __func__, body, res);
		para_put_item(msg_out, SOAR_ERR_PARA);
        return(atoi(SOAR_ERR_PARA));
	}
    para_copy_by_name(para, "sid", sid);
    para_free(para);

	if((res=_crack_sid(sid, &sid_time, &confirm, uuid, &hash)))
    {
        errlog("%s: _crack_sid(%s) fail, res:%d",
                __func__, sid, res);
        para_put_item(msg_out, SOAR_ERR_SESSION_SID_INVALID);
        return(atoi(SOAR_ERR_SESSION_SID_INVALID));
    }

    pthread_mutex_lock(&hash->lock);

    if((tree=rb_find(&hash->tree, uuid)))
    {
        user=tree_entry(tree, struct _user, tree);
        if(_user_timeout(hash, user) == IS_TRUE)
        {
            if(cnfg_info._errlog_.tracking)
                errlog("%s %s: uuid(%s) timeout, not found",
                        __FILE__, __func__, uuid);
            rtn=SOAR_ERR_SESSION_NOT_FOUND;
        }
        else if(user->sid_time != sid_time || user->confirm != confirm)
        {
            if(cnfg_info._errlog_.tracking)
                errlog("%s %s: uuid(%s) discord, "
                        "sid_time(%08X)-(%08X) confirm(%04X)-(%04X)",
                        __FILE__, __func__, uuid,
                        user->sid_time, sid_time,
                        user->confirm, confirm);
            rtn=SOAR_ERR_SESSION_SID_DISCORD;
        }
        else
        {
            rtn=_user_free(hash, user);
            if(rtn != (char *)SOAR_ERR_OK)
            {
                errlog("%s %s: _user_free fail, res:%d, IMPOSSIBLE",
                        __FILE__, __func__, res);
            }
            else if(cnfg_info._errlog_.tracking)
                errlog("%s %s: destroy uuid(%s)",
                        __FILE__, __func__, uuid);
        }
    }
    else
    {
        if(cnfg_info._errlog_.tracking)
            errlog("%s %s: uuid(%s) not found",
                    __FILE__, __func__, uuid);
        rtn=SOAR_ERR_SESSION_NOT_FOUND;
    }

#ifdef _MY_DEBUG
    _eliminate_timeout(hash, &res);
    pthread_mutex_unlock(&hash->lock);

    pthread_mutex_lock(&share_info.worker_info_lock);
    share_info.hash_users[hash->idx]=res;
    pthread_mutex_unlock(&share_info.worker_info_lock);
#else
    pthread_mutex_unlock(&hash->lock);
#endif

    para_insert_item(msg_out, rtn);
    return(atoi(rtn));
}/*}}}*/

SVC_FUNC(PUTKEY)
{/*{{{*/
    char para_desc[100];
    PARA para;
    char *rtn;
	char uuid[UUID_MAX_SIZE+1];
	char sid[SIDENC_MAX_SIZE+1];
	time_t sid_time;
    unsigned short confirm;
    struct _hash *hash;
    struct _user *user;
    struct rb_link *tree;
    int res;

    sprintf(para_desc,
            "sid(string:20-%d) "
            "keys(array){ key(string:1-%d) val(string:0-%d) }",
            SIDENC_MAX_SIZE, NAME_SIZE, MAP_VAL_MAX_SIZE);
    res=para_load(body, para_desc, &para);
    if(res)
	{
        if(res > 0)
            errlog("%s: body(%s) no.%d para invalid",
                    __func__, body, res);
        else
            errlog("%s: para_load body(%s) fail, res:%d",
                    __func__, body, res);
		para_put_item(msg_out, SOAR_ERR_PARA);
        return(atoi(SOAR_ERR_PARA));
	}
    para_copy_by_name(para, "sid", sid);

	if((res=_crack_sid(sid, &sid_time, &confirm, uuid, &hash)))
    {
        errlog("%s: _crack_sid(%s) fail, res:%d",
                __func__, sid, res);
        para_free(para);
        para_put_item(msg_out, SOAR_ERR_SESSION_SID_INVALID);
        return(atoi(SOAR_ERR_SESSION_SID_INVALID));
    }

    pthread_mutex_lock(&hash->lock);

    if((tree=rb_find(&hash->tree, uuid)))
    {
        user=tree_entry(tree, struct _user, tree);
        if(_user_timeout(hash, user) == IS_TRUE)
        {
            if(cnfg_info._errlog_.tracking)
                errlog("%s %s: uuid(%s) timeout, not found",
                        __FILE__, __func__, uuid);
            rtn=SOAR_ERR_SESSION_NOT_FOUND;
        }
        else if(user->sid_time != sid_time || user->confirm != confirm)
        {
            if(cnfg_info._errlog_.tracking)
                errlog("%s %s: uuid(%s) discord, "
                        "sid_time(%08X)-(%08X) confirm(%04X)-(%04X)",
                        __FILE__, __func__, uuid,
                        user->sid_time, sid_time,
                        user->confirm, confirm);
            rtn=SOAR_ERR_SESSION_SID_DISCORD;
        }
        else
        {
            int key_idx;
            int keys_num;
            char *keys;
            char key[NAME_SIZE+1];
            //char val[MAP_VAL_MAX_SIZE+1];
            char *val;
            char buf[20];

            ALLOC_MULTI_DOERR(val, char, MAP_VAL_MAX_SIZE+1,
                    errlog("%s: alloc val fail", __func__);
                    rtn=SOAR_ERR_RESOURCE;
                    goto UNLOCK);
            para_ref_by_name(para, "keys", &keys);
            keys_num=atoi(keys);
            for(key_idx=0; key_idx<keys_num; key_idx++)
            {
                sprintf(buf, "keys[%d].key", key_idx);
                para_copy_by_name(para, buf, key);
                trim_tail_space(key);
                sprintf(buf, "keys[%d].val", key_idx);
                para_copy_by_name(para, buf, val);
                trim_tail_space(val);
                rtn=_imp_put_key(user, key, val);
                if(rtn != (char *)SOAR_ERR_OK)
                {
                    errlog("%s: put_key(uuid:%s key:%s val:%s) "
                            "fail, res:%s, BAD",
                            __func__, uuid, key, val,
                            rtn);
                    free(val);
                    goto UNLOCK;
                }
            }
            free(val);
            rtn=SOAR_ERR_OK;
        }
    }
    else
    {
        if(cnfg_info._errlog_.tracking)
            errlog("%s: uuid(%s) not found",
                    __func__, uuid);
        rtn=SOAR_ERR_SESSION_NOT_FOUND;
    }

UNLOCK:
    pthread_mutex_unlock(&hash->lock);

    para_free(para);

    para_insert_item(msg_out, rtn);
    return(atoi(rtn));
}/*}}}*/

SVC_FUNC(GETKEY)
{/*{{{*/
    char para_desc[100];
    PARA para;
    char *rtn;
    struct _hash *hash;
    struct _user *user;
    struct rb_link *tree;
	char sid[SIDENC_MAX_SIZE+1];
	char uuid[UUID_MAX_SIZE+1];
	time_t sid_time;
    unsigned short confirm;
    int res;

    sprintf(para_desc,
            "sid(string:20-%d)"
            "keys(array){ key(string:1-%d) }",
            SIDENC_MAX_SIZE, NAME_SIZE);
    res=para_load(body, para_desc, &para);
    if(res)
	{
        if(res > 0)
            errlog("%s: body(%s) no.%d para invalid",
                    __func__, body, res);
        else
            errlog("%s: para_load body(%s) fail, res:%d",
                    __func__, body, res);
        para_put_item(msg_out, SOAR_ERR_PARA);
		return(atoi(SOAR_ERR_PARA));
	}
    para_copy_by_name(para, "sid", sid);

	if((res=_crack_sid(sid, &sid_time, &confirm, uuid, &hash)))
    {
        errlog("%s: _crack_sid(%s) fail, res:%d",
                __func__, sid, res);
        para_free(para);
        para_put_item(msg_out, SOAR_ERR_SESSION_SID_INVALID);
        return(atoi(SOAR_ERR_SESSION_SID_INVALID));
    }

    pthread_mutex_lock(&hash->lock);

    if((tree=rb_find(&hash->tree, uuid)))
    {
        user=tree_entry(tree, struct _user, tree);
        if(_user_timeout(hash, user) == IS_TRUE)
        {
            if(cnfg_info._errlog_.tracking)
                errlog("%s: uuid(%s) timeout, not found",
                        __func__, uuid);
            rtn=SOAR_ERR_SESSION_NOT_FOUND;
        }
        else if(user->sid_time != sid_time || user->confirm != confirm)
        {
            if(cnfg_info._errlog_.tracking)
                errlog("%s: uuid(%s) discord, "
                        "sid_time(%08X)-(%08X) confirm(%04X)-(%04X)",
                        __func__, uuid,
                        user->sid_time, sid_time,
                        user->confirm, confirm);
            rtn=SOAR_ERR_SESSION_SID_DISCORD;
        }
        else
        {
            int key_idx;
            int keys_num;
            char *keys;
            char key[NAME_SIZE+1];
            //char val[MAP_VAL_MAX_SIZE+1];
            char *val;
            char buf[20];

            ALLOC_MULTI_DOERR(val, char, MAP_VAL_MAX_SIZE+1,
                    errlog("%s: alloc val fail", __func__);
                    rtn=SOAR_ERR_RESOURCE;
                    goto UNLOCK);
            para_ref_by_name(para, "keys", &keys);
            para_put_item(msg_out, keys);
            keys_num=atoi(keys);
            for(key_idx=0; key_idx<keys_num; key_idx++)
            {
                sprintf(buf, "keys[%d].key", key_idx);
                res=para_copy_by_name(para, buf, key);
                trim_tail_space(key);
                rtn=_imp_get_key(user, key, val);
                if(rtn != (char *)SOAR_ERR_OK)
                {
                    if(cnfg_info._errlog_.tracking)
                        errlog("%s: get_key(uuid:%s key:%s val:%s) "
                                "fail, res:%s, BAD",
                                __func__, uuid, key, val,
                                rtn);
                    free(val);
                    CLR_BUF(msg_out);
                    goto UNLOCK;
                }
                para_put_item(msg_out, val);
            }
            free(val);
            rtn=SOAR_ERR_OK;
        }
    }
    else
    {
        if(cnfg_info._errlog_.tracking)
            errlog("%s: uuid(%s) not found",
                    __func__, uuid);
        rtn=SOAR_ERR_SESSION_NOT_FOUND;
    }

UNLOCK:
    pthread_mutex_unlock(&hash->lock);

    para_free(para);

    para_insert_item(msg_out, rtn);
    return(atoi(rtn));
}/*}}}*/

SVC_FUNC(MODKEY)
{/*{{{*/
    char para_desc[100];
    PARA para;
    char *rtn;
    struct _hash *hash;
    struct _user *user;
    struct rb_link *tree;
	char sid[SIDENC_MAX_SIZE+1];
	char uuid[UUID_MAX_SIZE+1];
	time_t sid_time;
    unsigned short confirm;
	char key[NAME_SIZE+1];
	int delta;
    int res;
    char *p;

    sprintf(para_desc,
            "sid(string:20-%d)"
            "key(string:1-%d)"
            "delta(number:1-%d)",
            SIDENC_MAX_SIZE,
            NAME_SIZE,
            LONG_NUMBER_SIZE);
    res=para_load(body, para_desc, &para);
    if(res)
	{
        if(res > 0)
            errlog("%s: body(%s) no.%d para invalid",
                    __func__, body, res);
        else
            errlog("%s: para_load body(%s) fail, res:%d",
                    __func__, body, res);
		para_put_item(msg_out, SOAR_ERR_PARA);
		return(atoi(SOAR_ERR_PARA));
	}
    para_copy_by_name(para, "sid", sid);
	para_copy_by_name(para, "key", key);
    trim_tail_space(key);
	para_ref_by_name(para, "delta", &p);
	delta=atoi(p);
    para_free(para);

	if((res=_crack_sid(sid, &sid_time, &confirm, uuid, &hash)))
    {
        errlog("%s: _crack_sid(%s) fail, res:%d",
                __func__, sid, res);
        para_free(para);
        para_put_item(msg_out, SOAR_ERR_SESSION_SID_INVALID);
        return(atoi(SOAR_ERR_SESSION_SID_INVALID));
    }

    pthread_mutex_lock(&hash->lock);

    if((tree=rb_find(&hash->tree, uuid)))
    {
        user=tree_entry(tree, struct _user, tree);
        if(_user_timeout(hash, user) == IS_TRUE)
        {
            if(cnfg_info._errlog_.tracking)
                errlog("%s: uuid(%s) timeout, not found",
                        __func__, uuid);
            rtn=SOAR_ERR_SESSION_NOT_FOUND;
        }
        else if(user->sid_time != sid_time || user->confirm != confirm)
        {
            if(cnfg_info._errlog_.tracking)
                errlog("%s: uuid(%s) discord, "
                        "sid_time(%08X)-(%08X) confirm(%04X)-(%04X)",
                        __func__, uuid,
                        user->sid_time, sid_time,
                        user->confirm, confirm);
            rtn=SOAR_ERR_SESSION_SID_DISCORD;
        }
        else
        {
            rtn=_imp_mod_key(user, key, delta);
            if(rtn != (char *)SOAR_ERR_OK)
            {
                errlog("%s: mod_key(uuid:%s key:%s delta:%d) "
                        "fail, res:%s, BAD",
                        __func__, uuid, key, delta,
                        rtn);
            }
        }
    }
    else
    {
        if(cnfg_info._errlog_.tracking)
            errlog("%s: uuid(%s) not found", __func__, uuid);
        rtn=SOAR_ERR_SESSION_NOT_FOUND;
    }

    pthread_mutex_unlock(&hash->lock);

    para_insert_item(msg_out, rtn);
    return(atoi(rtn));
}/*}}}*/

SVC_FUNC(DESTROYKEY)
{/*{{{*/
    char para_desc[100];
    PARA para;
    char *rtn;
	char sid[SIDENC_MAX_SIZE+1];
	char uuid[UUID_MAX_SIZE+1];
	time_t sid_time;
    unsigned short confirm;
	char key[NAME_SIZE+1];
    struct _hash *hash;
    struct _user *user;
    struct rb_link *tree;
    int res;

    sprintf(para_desc, "sid(string:20-%d) key(string:1-%d)",
            SIDENC_MAX_SIZE, NAME_SIZE);
    res=para_load(body, para_desc, &para);
    if(res)
	{
        if(res > 0)
            errlog("%s: body(%s) no.%d para invalid",
                    __func__, body, res);
        else
            errlog("%s: para_load body(%s) fail, res:%d",
                    __func__, body, res);
		para_put_item(msg_out, SOAR_ERR_PARA);
		return(atoi(SOAR_ERR_PARA));
	}
    para_copy_by_name(para, "sid", sid);
	para_copy_by_name(para, "key", key);
    trim_tail_space(key);
    para_free(para);

	if((res=_crack_sid(sid, &sid_time, &confirm, uuid, &hash)))
    {
        errlog("%s: _crack_sid(%s) fail, res:%d",
                __func__, sid, res);
        para_free(para);
        para_put_item(msg_out, SOAR_ERR_SESSION_SID_INVALID);
        return(atoi(SOAR_ERR_SESSION_SID_INVALID));
    }

    pthread_mutex_lock(&hash->lock);

    if((tree=rb_find(&hash->tree, uuid)))
    {
        user=tree_entry(tree, struct _user, tree);
        if(_user_timeout(hash, user) == IS_TRUE)
        {
            if(cnfg_info._errlog_.tracking)
                errlog("%s: uuid(%s) timeout, not found",
                        __func__, uuid);
            rtn=SOAR_ERR_SESSION_NOT_FOUND;
        }
        else if(user->sid_time != sid_time || user->confirm != confirm)
        {
            if(cnfg_info._errlog_.tracking)
                errlog("%s: uuid(%s) discord, "
                        "sid_time(%08X)-(%08X) confirm(%04X)-(%04X)",
                        __func__, uuid,
                        user->sid_time, sid_time,
                        user->confirm, confirm);
            rtn=SOAR_ERR_SESSION_SID_DISCORD;
        }
        else
        {
            rtn=_imp_del_key(user, key);
            if(rtn != (char *)SOAR_ERR_OK)
            {
                errlog("%s: mod_key(uuid:%s key:%s) fail, res:%d, BAD",
                        __func__, uuid, key, res);
            }
        }
    }
    else
    {
        if(cnfg_info._errlog_.tracking)
            errlog("%s: uuid(%s) not found", __func__, uuid);
        rtn=SOAR_ERR_SESSION_NOT_FOUND;
    }

    pthread_mutex_unlock(&hash->lock);

    para_insert_item(msg_out, rtn);
    return(atoi(rtn));
}/*}}}*/

#define PLAIN_SIZE              500

SVC_FUNC(ENCRYPT)
{/*{{{*/
    char *rtn;
    int res;

    char para_desc[50];
    PARA para;
    sprintf(para_desc, "plain(string:1-%d)", PLAIN_SIZE);
    res=para_load(body, para_desc, &para);
    if(res)
	{
        if(res > 0)
            errlog("%s: msg(%.20s) no.%d para invalid",
                    __func__, body, res);
        else
            errlog("%s: para_load msg(%.20s) fail, res:%d",
                    __func__, body, res);
		para_put_item(msg_out, SOAR_ERR_PARA);
        return(atoi(SOAR_ERR_PARA));
	}
    char *plain;
    para_ref_by_name(para, "plain", &plain);

    unsigned char *enc=NULL;
    char *code=NULL;
    ALLOC_MULTI_DOERR(enc, unsigned char, KB(1),
            errlog("%s: alloc enc fail", __func__);
            rtn=SOAR_ERR_RESOURCE; goto GO_OUT);
    ALLOC_MULTI_DOERR(code, char, KB(2),
            errlog("%s: alloc code fail", __func__);
            rtn=SOAR_ERR_RESOURCE; goto GO_OUT);

    int plain_size;
    random_padding((unsigned char *)plain, strlen(plain),
            (unsigned char *)code, &plain_size);
    int enc_size;
    res=ossl_sym_encrypt(sesskey,
            (unsigned char *)code, plain_size,
            enc, &enc_size);
    if(res)
    {
        errlog("%s: ossl_sym_encrypt fail, res:%d",
                __func__, res);
        rtn=SOAR_ERR_SESSION_ENCRYPT; goto GO_OUT;
    }
    //expand_to_str(enc, enc_size, code);
    int code_size;
    res=ossl_encode(OSSL_ENCODE_BASE64,
            enc, enc_size,
            code, &code_size);
    if(res)
    {
        errlog("%s: ossl_encode fail, res:%d",
                __func__, res);
        rtn=SOAR_ERR_SESSION_ENCRYPT; goto GO_OUT;
    }
    base64_unwrap(code, code);
    para_put_item(msg_out, code);

    rtn=SOAR_ERR_OK;

GO_OUT:
    para_free(para);
    FREE(enc);
    FREE(code);
    para_insert_item(msg_out, rtn);
    return(atoi(rtn));
}/*}}}*/

SVC_FUNC(DECRYPT)
{/*{{{*/
    char *rtn;
    int res;

    char para_desc[50];
    PARA para;
    sprintf(para_desc, "code(string:2-%d)", KB(1));
    res=para_load(body, para_desc, &para);
    if(res)
	{
        if(res > 0)
            errlog("%s: msg(%.20s) no.%d para invalid",
                    __func__, body, res);
        else
            errlog("%s: para_load msg(%.20s) fail, res:%d",
                    __func__, body, res);
		para_put_item(msg_out, SOAR_ERR_PARA);
        return(atoi(SOAR_ERR_PARA));
	}
    char *code=NULL;
    unsigned char *enc=NULL;
    char *plain=NULL;
    ALLOC_MULTI_DOERR(code, char, KB(2),
            errlog("%s: alloc code fail", __func__);
            para_free(para);
            rtn=SOAR_ERR_RESOURCE; goto GO_OUT);
    para_copy_by_name(para, "code", code);
    para_free(para);
    ALLOC_MULTI_DOERR(enc, unsigned char, KB(1),
            errlog("%s: alloc enc fail", __func__);
            rtn=SOAR_ERR_RESOURCE; goto GO_OUT);
    ALLOC_MULTI_DOERR(plain, char, KB(1),
            errlog("%s: alloc plain fail", __func__);
            rtn=SOAR_ERR_RESOURCE; goto GO_OUT);

    base64_wrap(code, code);
    int enc_size;
    res=ossl_decode(OSSL_ENCODE_BASE64,
            code, strlen(code),
            enc, &enc_size);
    if(res || enc_size < 1 || enc_size > PLAIN_SIZE)
    {
        errlog("%s: ossl_decode fail, enc_size:%d res:%d",
                __func__, enc_size, res);
        rtn=SOAR_ERR_SESSION_DECRYPT; goto GO_OUT;
    }
//    enc_size=strlen(code)/2;
//    if(!collapse_from_str(code, enc_size*2, enc))
//    {
//        errlog("%s: code(%.30s) collapse fail",
//                __func__, code);
//        rtn=SOAR_ERR_SESSION_DECRYPT; goto GO_OUT;
//    }
    int plain_size;
    res=ossl_sym_decrypt(sesskey, enc, enc_size,
            (unsigned char *)plain, &plain_size);
    if(res)
    {
        errlog("%s: ossl_sym_decrypt fail, res:%d, BAD",
                __func__, res);
        rtn=SOAR_ERR_SESSION_DECRYPT; goto GO_OUT;
        return(res);
    }
    random_unpadding((unsigned char *)plain, plain_size,
            (unsigned char *)plain, &plain_size);
    plain[plain_size]=ZERO;
    para_put_item(msg_out, plain);

    rtn=SOAR_ERR_OK;

GO_OUT:
    FREE(code);
    FREE(enc);
    FREE(plain);
    para_insert_item(msg_out, rtn);
    return(atoi(rtn));
}/*}}}*/
